package chagine.mdt.utils;

import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author zevin
 * @date 2023/1/31 9:20
 */
@Slf4j
public class LambdaUtils {

    /**
     * 缓存类-Lambda的映射关系
     */
    private static Map<Class<?>, SerializedLambda> CLASS_LAMDBA_CACHE = new ConcurrentHashMap<>();

    /***
     * 转换方法引用为属性名
     * @param fn
     * @return
     */
//    public static String _convertToFieldName(GetterS<?, ?> fn) {
//        SerializedLambda lambda = getSerializedLambda(fn);
//        String methodName = lambda.getImplMethodName();
//        String prefix = null;
//        if(methodName.startsWith("get")){
//            prefix = "get";
//        }
//        else if(methodName.startsWith("is")){
//            prefix = "is";
//        }
//        if(prefix == null){
//            log.warn("无效的getter方法: "+methodName);
//        }
//        // 截取get/is之后的字符串并转换首字母为小写（S为diboot项目的字符串工具类，可自行实现）
//        return _convertToFieldName(methodName, prefix);
//    }

    /***
     * 转换setter方法引用为属性名
     * @param fn
     * @return
     */
//    public static <T,R> String _convertToFieldName(SetterS<T,R> fn) {
//        SerializedLambda lambda = getSerializedLambda(fn);
//        String methodName = lambda.getImplMethodName();
//        if(!methodName.startsWith("set")){
//            log.warn("无效的setter方法: "+methodName);
//        }
//        // 截取set之后的字符串并转换首字母为小写（S为diboot项目的字符串工具类，可自行实现）
//        return _convertToFieldName(methodName, "set");
//    }

    /**
     * methodName ---> fieldName
     * @param methodName
     * @param prefix
     * @return
     */
    private static String _convertToFieldName(String methodName, String prefix) {
        int prefixLength = prefix.length();

        StringBuilder sb = new StringBuilder();
        sb.append(methodName.charAt(prefixLength));
        for (int i = prefixLength + 1; i < methodName.length(); i++) {
            sb.append(methodName.charAt(i));
        }
        return sb.toString();
    }

    /***
     * 获取类对应的Lambda
     * @param fn
     * @return
     */
    private static SerializedLambda getSerializedLambda(Serializable fn){
        //先检查缓存中是否已存在
        SerializedLambda lambda = CLASS_LAMDBA_CACHE.get(fn.getClass());
        if(lambda == null){
            try{//提取SerializedLambda并缓存
                Method method = fn.getClass().getDeclaredMethod("writeReplace");
                method.setAccessible(Boolean.TRUE);
                lambda = (SerializedLambda) method.invoke(fn);
                CLASS_LAMDBA_CACHE.put(fn.getClass(), lambda);
            }
            catch (Exception e){
                log.error("获取SerializedLambda异常, class="+fn.getClass().getSimpleName(), e);
            }
        }
        return lambda;
    }


    //https://blog.csdn.net/baiyuxuan123123/article/details/123056115

    static Pattern RETURN_TYPE_PATTERN = Pattern.compile("\\(.*\\)L(.*);");
    static Pattern PARAMETER_TYPE_PATTERN = Pattern.compile("\\((.*)\\).*");

    /**
     * 获取Lambda表达式返回类型
     */
    public static Class<?> getReturnType(Serializable fn) {
        String expr = getSerializedLambda(fn).getInstantiatedMethodType();
        Matcher matcher = RETURN_TYPE_PATTERN.matcher(expr);
        if (!matcher.find() || matcher.groupCount() != 1) {
            throw new RuntimeException("获取Lambda信息失败");
        }
        String className = matcher.group(1).replace("/", ".");
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("无法加载类", e);
        }
    }

    /**
     * 获取Lambda表达式的参数类型
     */
    public static List<Class<?>> getParameterTypes(Serializable fn) {
        String expr = getSerializedLambda(fn).getInstantiatedMethodType();
        Matcher matcher = PARAMETER_TYPE_PATTERN.matcher(expr);
        if (!matcher.find() || matcher.groupCount() != 1) {
            throw new RuntimeException("获取Lambda信息失败");
        }
        expr = matcher.group(1);

        return Arrays.stream(expr.split(";"))
                .filter(s -> !s.isEmpty())
                .map(s -> s.replace("L", "").replace("/", "."))
                .map(s -> {
                    try {
                        return Class.forName(s);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException("无法加载类", e);
                    }
                })
                .collect(Collectors.toList());
    }
}